﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;
using System.Xml.Serialization;
using System.IO;
using SkeletalAnimation3D;
using System.Xml;
using System.Windows.Markup;
using Animator3D.Controls;
using Animator3D.ObjectModel;
using Animator3D.Importers;
using Microsoft.Win32;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reflection;
using Hardcodet.Wpf.GenericTreeView;
using SkeletalAnimation3D.Core;

namespace Animator3D
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Document _document = null;

        // Just for testing purposes. Remove from final build.
        static MainWindow()
        {
            using (Stream stream = File.Create("testanimlib.a3dx"))
            {
                BoneAnimationLibrary testLib = BoneAnimationLibrary.MakeTestLib();
                testLib.Save(stream);
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            NewDocument();
            
        }

        private void NewDocument()
        {
            UnbindDocument();
            _document = new Document();
            BindDocument();
        }

        private void LoadDocument()
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "a3dx files|*.a3dx|obj files|*.obj|All files|*.*";
            bool? result = openFileDialog.ShowDialog();
            if (result.HasValue && result.Value)
            {
                Document document = null;
                if (openFileDialog.FileName.EndsWith(".a3dx", StringComparison.CurrentCultureIgnoreCase))
                {
                    using (Stream stream = File.OpenRead(openFileDialog.FileName))
                    {
                        document = Document.Load(stream, null);
                    }
                }
                else if (openFileDialog.FileName.EndsWith(".obj", StringComparison.CurrentCultureIgnoreCase))
                {
                    using (FileStream stream = File.OpenRead(openFileDialog.FileName))
                    {
                        BoneAnimationLibrary animationLibrary = ObjImporter.Read(stream, true);
                        document = new Document(animationLibrary);
                    }
                }
                if (document != null)
                {

                    UnbindDocument();
                    _document = document;
                    BindDocument();
                }
            }
        }

        private void BindDocument()
        {
            DataContext = _document;
            //viewport2DTopLeftPose.BindToDocument(_document);
            //viewport2DTopRightPose.BindToDocument(_document);
            //viewport2DBottomLeftPose.BindToDocument(_document);
            
            skeletonTreeView.DataContext = _document;
            Binding binding = new Binding("CurrentAnimationOffset");
            binding.Source = _document;
            binding.Mode = BindingMode.TwoWay;
            TimeSlider.SetBinding(Slider.ValueProperty, binding);

            //todo: replace setting by binding
            MeshGeometry3D meshGeometry = new MeshGeometry3D();
            meshGeometry.Positions = _document.MeshPoints;
            meshGeometry.Normals = _document.MeshNormals;
            meshGeometry.TextureCoordinates = _document.TextureCoordinates;
            displayModelPose.Geometry = meshGeometry;
            displayModelPose.Material = _document.Material;

            binding = new Binding("Animations");
            binding.Source = _document;
            binding.Mode = BindingMode.OneWay;
            animationsComboBox.SetBinding(ComboBox.ItemsSourceProperty, binding);
            
            binding = new Binding("CurrentAnimation");
            binding.Source = _document;
            binding.Mode = BindingMode.TwoWay;
            animationsComboBox.SetBinding(ComboBox.SelectedItemProperty, binding);

            TimeSlider.DataContext = _document.Animations;

        }

        private void StartAnimationButton_Click(object sender, RoutedEventArgs e)
        {
        }

        private void TaskTab_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Contains(AnimateTaskTab))
            {
                if (KeyframeEditor != null)
                {
                    KeyframeEditor.Visibility = Visibility.Visible;
                }
                if(PoseEditor != null)
                {
                    PoseEditor.Visibility = Visibility.Collapsed;
                }
            }
            if (e.AddedItems.Contains(PoseTaskTab))
            {
                if (KeyframeEditor != null)
                {
                    KeyframeEditor.Visibility = Visibility.Collapsed;
                }
                if (PoseEditor != null)
                {
                    PoseEditor.Visibility = Visibility.Visible;
                }
            }
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            TaskTab.SelectedItem = AnimateTaskTab;
            TaskTab.SelectedItem = PoseTaskTab;

            Dialogs.About about = new Dialogs.About();
            about.ShowDialog();
        }

        private void TimeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            Point3DCollection points;
            Vector3DCollection normals;
            _document.GetAnimationStill(e.NewValue, out points, out normals); 
            MeshGeometry3D meshGeometry = new MeshGeometry3D();
            meshGeometry.Positions = points;
            meshGeometry.Normals = normals;
            meshGeometry.TextureCoordinates = _document.TextureCoordinates;
            displayModelPose.Geometry = meshGeometry;
            displayModelPose.Material = _document.Material;
        }

        private void UnbindDocument()
        {
            DataContext = null;
            //viewport2DTopLeftPose.UnbindFromDocument();
            //viewport2DTopRightPose.UnbindFromDocument();
            //viewport2DBottomLeftPose.UnbindFromDocument();

            skeletonTreeView.DataContext = null;
            BindingOperations.ClearBinding(TimeSlider, Slider.ValueProperty);
        }

        private void StopAnimationButton_Click(object sender, RoutedEventArgs e)
        {
           
        }

        private void PauseAnimationButton_Click(object sender, RoutedEventArgs e)
        {
        }

        private void FirstAnimationFrameButton_Click(object sender, RoutedEventArgs e)
        {
         
        }

        private void LastAnimationFrameButton_Click(object sender, RoutedEventArgs e)
        {
         
        }

        private void skeletonTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (e != null)
            {
                BoneInfo boneInfo = e.NewValue as BoneInfo;
                if (boneInfo != null)
                {
                    if (String.Compare(boneInfo.Name, "Root", true) == 0)
                    {
                        BoneNameTextBox.IsEnabled = false;
                        RenameBoneButton.IsEnabled = false;
                    }
                    else
                    {
                        BoneNameTextBox.IsEnabled = true;
                        RenameBoneButton.IsEnabled = true;
                    }

                    _document.SelectBone(boneInfo.Id);
                }
            }
        }

        private enum ViewMode
        {
            None,
            Panning
        }

        private enum PosingMode
        {
            None,
            MovingBone,
            AddingBones,
            SelectingPoints,
            DeselectingPoints
        }

        private ViewMode _viewMode = ViewMode.None;
        private PosingMode _posingMode = PosingMode.None;
        private Viewport2D _dragViewport = null;
        private Point _dragStart;

        private void viewport2D_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Viewport2D viewport = sender as Viewport2D;
            if (viewport != null)
            {
                if (e.RightButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
                {
                    viewport.Cursor = Cursors.SizeAll;
                    _dragViewport = viewport;
                    _dragStart = e.GetPosition(viewport);
                    _viewMode = ViewMode.Panning;
                }
                else if (_posingMode == PosingMode.SelectingPoints && e.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
                {
                    _dragViewport = viewport;
                    _dragStart = e.GetPosition(viewport);
                    _posingMode = PosingMode.SelectingPoints;
                }
                else if (_posingMode == PosingMode.DeselectingPoints && e.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
                {
                    _dragViewport = viewport;
                    _dragStart = e.GetPosition(viewport);
                    _posingMode = PosingMode.DeselectingPoints;
                }
                else if (_posingMode == PosingMode.MovingBone && e.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
                {
                    _dragViewport = viewport;
                    _dragStart = e.GetPosition(viewport);
                }
            }
        }

        private void viewport2D_MouseMove(object sender, MouseEventArgs e)
        {
            Viewport2D viewport = sender as Viewport2D;
            if (_viewMode == ViewMode.Panning && _dragViewport == viewport && e.RightButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
            {
                Point dragEnd = e.GetPosition(viewport);
                Vector offset = dragEnd - _dragStart;
                _dragStart = dragEnd;
                viewport.MoveCamera(offset);
            }
            else if (_posingMode == PosingMode.SelectingPoints && _dragViewport == viewport && e.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
            {
                Point dragEnd = e.GetPosition(viewport);
                viewport.ShowSelector(_dragStart, dragEnd);
            }
            else if (_posingMode == PosingMode.DeselectingPoints && _dragViewport == viewport && e.LeftButton == MouseButtonState.Pressed && Keyboard.Modifiers == ModifierKeys.None)
            {
                Point dragEnd = e.GetPosition(viewport);
                viewport.ShowDeselector(_dragStart, dragEnd);
            }
            else if (_posingMode == PosingMode.MovingBone && _dragViewport == viewport && e.LeftButton == MouseButtonState.Pressed)
            {
                Point dragEnd = e.GetPosition(viewport);
                Vector3D offset = viewport.Unproject(dragEnd - _dragStart);
                _document.MoveBone(_document.SelectedBoneId, offset);
                _dragStart = dragEnd;
            }
        }


        private void viewport2D_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            Viewport2D viewport = sender as Viewport2D;
            if (viewport != null)
            {
                double factor = 1;
                if (Keyboard.Modifiers == ModifierKeys.Alt)
                {
                    factor /= 3;
                }
                if (Keyboard.Modifiers == ModifierKeys.Control)
                {
                    factor *= 3;
                }
                viewport.ZoomCamera(factor * -e.Delta / Mouse.MouseWheelDeltaForOneLine);
            }
        }

        private void viewport2D_MouseUp(object sender, MouseEventArgs e)
        {
            Viewport2D viewport = sender as Viewport2D;
            if (viewport != null)
            {
                if (_viewMode == ViewMode.Panning && e.RightButton == MouseButtonState.Released && Keyboard.Modifiers == ModifierKeys.None)
                {
                    viewport.Cursor = Cursors.Arrow;
                    _dragViewport = null;
                    _viewMode = ViewMode.None;
                }
                else if (_posingMode == PosingMode.SelectingPoints && _dragViewport == viewport && e.LeftButton == MouseButtonState.Released && Keyboard.Modifiers == ModifierKeys.None)
                {
                    Int32Collection selectedIds = viewport.GetPointIdsInSelection();
                    _document.AddSelectedPointIds(selectedIds);
                    viewport.HideSelector();
                }
                else if (_posingMode == PosingMode.DeselectingPoints && _dragViewport == viewport && e.LeftButton == MouseButtonState.Released && Keyboard.Modifiers == ModifierKeys.None)
                {
                    Int32Collection deselectedIds = viewport.GetPointIdsInDeselection();
                    _document.RemoveSelectedPointIds(deselectedIds);
                    viewport.HideDeselector();
                }
                else if (_posingMode == PosingMode.MovingBone && _dragViewport == viewport && e.LeftButton == MouseButtonState.Released && Keyboard.Modifiers == ModifierKeys.None)
                {
                    _dragViewport = null;
                }
                else if (_posingMode == PosingMode.AddingBones && e.LeftButton == MouseButtonState.Released && Keyboard.Modifiers == ModifierKeys.None)
                {
                    Bone parentBone = _document.GetSelectedBone();
                    Point3D newBonePosition = viewport.Unproject(e.GetPosition(viewport));
                    switch (viewport.CameraOrientation)
                    {
                        case CameraOrientations.Front:
                        case CameraOrientations.Back:
                            newBonePosition.Z = parentBone.Position.Z;
                            break;
                        case CameraOrientations.Left:
                        case CameraOrientations.Right:
                            newBonePosition.X = parentBone.Position.X;
                            break;
                        case CameraOrientations.Top:
                        case CameraOrientations.Bottom:
                            newBonePosition.Y = parentBone.Position.Y;
                            break;
                        default:
                            break;
                    }
                    _document.AddBone(parentBone.Id, "Child " + (parentBone.ChildBones == null ? 0 : parentBone.ChildBones.Count), newBonePosition, parentBone.Size);
                }
            }
        }


        private void SetViewportsCursors(Cursor cursor)
        {
            viewport2DTopLeftPose.Cursor = cursor;
            viewport2DTopRightPose.Cursor = cursor;
            viewport2DBottomLeftPose.Cursor = cursor;
        }

        private bool SwitchToMode(PosingMode posingMode)
        {
            bool switched = false;
            switch (posingMode)
            {
                case PosingMode.None:
                    AnimateTaskTab.IsEnabled = true;
                    HelpTextBlock.Text = "";
                    SetViewportsCursors(Cursors.Arrow);
                    switched = true;
                    break;
                case PosingMode.MovingBone:
                    AnimateTaskTab.IsEnabled = false;
                    SetViewportsCursors(Cursors.SizeAll);
                    HelpTextBlock.Text = "Drag the bone to the desired position in the views and press the Move button again when done.";
                    switched = true;
                    break;
                case PosingMode.AddingBones:
                    AnimateTaskTab.IsEnabled = false;
                    HelpTextBlock.Text = "Click in the views to place the new bones and press the Add Bones button again when done.";
                    switched = true;
                    break;
                case PosingMode.SelectingPoints:
                    AnimateTaskTab.IsEnabled = false;
                    HelpTextBlock.Text = "Selecting";
                    switched = true;
                    break;
                case PosingMode.DeselectingPoints:
                    AnimateTaskTab.IsEnabled = false;
                    HelpTextBlock.Text = "Deselecting";
                    switched = true;
                    break;
                default:
                    break;
            }
            if (switched)
            {
                _posingMode = posingMode;
            }
            return switched;
        }

        private void SetBoneSizeButton_Click(object sender, RoutedEventArgs e)
        {
            //todo: add input validation
            _document.SetBoneSize(_document.SelectedBoneId, double.Parse(BoneSizeTextBox.Text));
        }

        private void RenameBoneButton_Click(object sender, RoutedEventArgs e)
        {
            //todo: add input validation
            _document.SetBoneName(_document.SelectedBoneId, BoneNameTextBox.Text);
        }

        private void MoveBoneButton_Click(object sender, RoutedEventArgs e)
        {
            if (MoveBoneButton.IsChecked.HasValue && MoveBoneButton.IsChecked.Value)
            {
                SwitchToMode(PosingMode.MovingBone);

            }
            else
            {
                SwitchToMode(PosingMode.None);

            }
        }

        private void AddBoneButton_Click(object sender, RoutedEventArgs e)
        {
            if (AddBoneButton.IsChecked.HasValue && AddBoneButton.IsChecked.Value)
            {
                SwitchToMode(PosingMode.AddingBones);

            }
            else
            {
                SwitchToMode(PosingMode.None);

            }
        }

        private void skeletonTreeView_SelectedItemChanged(object sender, RoutedTreeItemEventArgs<Bone> e)
        {
            if (e != null)
            {
                Bone bone = e.NewItem;
                if (bone != null)
                {
                    if (String.Compare(bone.Name, "Root", true) == 0)
                    {
                        BoneNameTextBox.IsEnabled = false;
                        RenameBoneButton.IsEnabled = false;
                    }
                    else
                    {
                        BoneNameTextBox.IsEnabled = true;
                        RenameBoneButton.IsEnabled = true;
                    }

                    _document.SelectBone(bone.Id);
                }
            }
        }

        private void ShowAttachedPointsButton_Click(object sender, RoutedEventArgs e)
        {
            if(ShowAttachedPointsButton.IsChecked.HasValue && ShowAttachedPointsButton.IsChecked.Value)
            {
                _document.ShowAttachedPoints = true;

            }
            else
            {
                _document.ShowAttachedPoints = false;

            }
        }

        private void NewDocumentButton_Click(object sender, RoutedEventArgs e)
        {
            NewDocument();
        }

        private void OpenDocumentButton_Click(object sender, RoutedEventArgs e)
        {
            LoadDocument();
        }

        private void SelectPointsButton_Click(object sender, RoutedEventArgs e)
        {
            if (SelectPointsButton.IsChecked.HasValue && SelectPointsButton.IsChecked.Value)
            {
                SwitchToMode(PosingMode.SelectingPoints);
            }
            else
            {
                SwitchToMode(PosingMode.None);
            }
        }

        private void DeselectPointsButton_Click(object sender, RoutedEventArgs e)
        {
            if (DeselectPointsButton.IsChecked.HasValue && DeselectPointsButton.IsChecked.Value)
            {
                SwitchToMode(PosingMode.DeselectingPoints);
            }
            else
            {
                SwitchToMode(PosingMode.None);
            }

        }

        private void AttachPointsButton_Click(object sender, RoutedEventArgs e)
        {
            if(_document.SelectedBoneId != -1 && _document.SelectedPointIds != null && _document.SelectedPointIds.Count > 0)
            {
                _document.AttachPointsToBone(_document.SelectedBoneId, _document.SelectedPointIds);
            }
        }

        private void DetachPointsButton_Click(object sender, RoutedEventArgs e)
        {
            if (_document.SelectedBoneId != -1 && _document.SelectedPointIds != null && _document.SelectedPointIds.Count > 0)
            {
                _document.DetachPointsFromBone(_document.SelectedBoneId, _document.SelectedPointIds);
            }
        }

        private void SetWeightButton_Click(object sender, RoutedEventArgs e)
        {
            if (_document.SelectedBoneId != -1 && _document.SelectedPointIds != null && _document.SelectedPointIds.Count > 0)
            {
                _document.SetPointWeightsOnBone(_document.SelectedBoneId, _document.SelectedPointIds, WeightSlider.Value / (WeightSlider.Maximum - WeightSlider.Minimum));
            }
        }

        private void NewAnimationButton_Click(object sender, RoutedEventArgs e)
        {
            //TODO _document.AddAnimation("Animation - ");
        }

        

    }
}
